      
//nativeBus.js

/**
 * @file H5与原生的交互方法（h5端）
 * @desc H5与原生的交互方法;
 * 为H5端使用，原生端也需进行相应的配置;
 * 原生端暴露给 WEBVIEW 的方法必须为taiYYNative;
 * @version 1.0.1
 * @author suwj07
 */
 (function(){
    
    /**
     * @constant {string}
     * @default [NATIVE BUS]
     * @desc 统一的控制台打印前缀
     */
    var S_LOG_PREFIX='[NATIVE BUS]';

    /**
     * @desc 当前ID索引;
     * @type {number}
     */ 
    var sCurIdIndex=0;

    /**
     * @global
     * @desc 调用原生方法（IOS 安卓）
     * @author suwj07
     * @param {Object} op -  
     * @param {String} op.serviceName - 服务名称
     * @param {Function} [op.success] - 成功回调
     * @param {Function} [op.fail] - 失败回调
     * @param {Number} [op.timeout=0] - 超时时间（ms）,为0则无超时报错
     * @param {*} [op.data] - 传参
     * @example   
     * window.tyyNativeBus({
     *  serviceName:'yourNativeServiceName',
     *  success:function(res){},
     *  fail:function(error){},
     *  timeout:0,//0为不设置超时时间
     *  data:''//any
     * })
     */ 
    
     this.tyyNativeBus=function (op){
        if(Object.prototype.toString.call(op) != '[object Object]'){
            console.error(S_LOG_PREFIX,'第一个参数必须为对象')
            return
        }
        if(!op.serviceName){
            console.error(S_LOG_PREFIX,'找不到serviceName')
            return
        }
        if(typeof op.serviceName!='string'){
            console.error(S_LOG_PREFIX,'serviceName 必须为字符串')
            return
        }
        var timeObj=''
        var serviceName = op.serviceName;
        var success = op.success;
        var fail = op.fail;
        var complete = op.complete;
        var data = op.data||'';
        var timeout = op.timeout||0;
        var obj={'functionName':serviceName,'parameter':data}
        
        var SUCCESS_PREFIX='NSS_S'
        var FAIL_PREFIX='NSS_F'
        var id=getId()
        function clearFn(id){
            fail=null
            success=null
            complete=null
            timeObj&&clearTimeout(timeObj)
            if(SUCCESS_PREFIX+id in window){
                delete window[SUCCESS_PREFIX+id]
            }
            if(FAIL_PREFIX+id in window){
                delete window[FAIL_PREFIX+id]
            }
        }
        function getId(){
            sCurIdIndex=sCurIdIndex+1;
            return new Date().getTime()+'_'+Math.round(Math.random()*1000000)+'_'+sCurIdIndex;
        }
        
        function resChange(data){
            var res=data
            try{
                if(Object.prototype.toString.call(res)=='[object String]'){
                    if(/^\{([\s\S])*\}$/.test(res)||/^\[([\s\S])*\]$/.test(res)){//json字符串
                        try{
                            res=JSON.parse(res)
                        }catch(e){
                            res= JSON.parse(res.replace(/[\n+]/g,'\\n').replace(/[\r+]/g,'\\r'))
                        }
                    }else{
                        res=JSON.parse(data)//强行尝试转json
                    }
                }
            }catch(e){
                
            }
            return res
        }
        if(success&&typeof success=='function'){
            obj['callback']=SUCCESS_PREFIX+id
            window[obj['callback']]=function(arg){
                success&&success(resChange(arg))
                complete&&complete()
                clearFn(id)
            }
        }
        if(fail&&typeof fail=='function'){
            obj['fail']=FAIL_PREFIX+id
            window[obj['fail']]=function(arg){
                fail&&fail(resChange(arg))
                complete&&complete()
                clearFn(id)
            }
        }
        try{
            if(_hasTaiYYNativeMethod()){
                _taiYYNativeMethod(JSON.stringify(obj))
                if(timeout){
                    timeObj=setTimeout(function(){
                        window[obj['fail']]&& window[obj['fail']]('service timeout')
                    },timeout)
                } 
            }else{
                window[obj['fail']]&& window[obj['fail']]('can not find taiYYNativeMethod')
            }
        }catch(e){
            console.warn(S_LOG_PREFIX,e)
            if(obj['fail'] in window){
                window[obj['fail']](e)
            }
        }
    }


    /**
     * @method 是否有原生通讯方法
     * @private
     * @description 判断是否有原生方法
     * @returns {Boolean} 如果有返回true , 否则返回false
    */
    function _hasTaiYYNativeMethod(){
        try{
            var method=''
            var modName='taiYYNative'
            if(modName in window&&typeof window[modName]=='function'){
                method=window[modName]
            }else if('webkit' in window&&'messageHandlers' in window.webkit&&modName in window.webkit.messageHandlers&&'postMessage' in window.webkit.messageHandlers[modName]){
                method = window.webkit.messageHandlers[modName].postMessage
            }else if('androidTyyCallback' in window&&modName in window.androidTyyCallback){
                method = function(a){//android 需要包一层函数
                    window.androidTyyCallback[modName](a)
                }
            }
            if(method&&typeof method=='function'){
                return true

            }else{
                return false
            }
        }catch(e){
            console.log(S_LOG_PREFIX,'hasTaiYYNativeMethod:',e)
            return false
        }         
    }

    /**
     * @method 原生通讯方法
     * @private
     * @description 调用原生方法
     * @author suwj07
     * @param {Object} op 
     * @example   
     * var op={
     *  functionName:'xx'
     *  callback:function(){},
     *  fail:function(){},
     *  parameter:''//any
     * }
     * _taiYYNativeMethod(JSON.stringify(op));  
     */
    function _taiYYNativeMethod(op){
        try{

            if('taiYYNative' in window&&typeof window.taiYYNative=='function'){
                window.taiYYNative(op)
            }else if('webkit' in window&&'messageHandlers' in window.webkit&&'taiYYNative' in window.webkit.messageHandlers&&'postMessage' in window.webkit.messageHandlers.taiYYNative){
                window.webkit.messageHandlers.taiYYNative.postMessage(op)
            }else if('androidTyyCallback' in window&&'taiYYNative' in window.androidTyyCallback){
                
                var fn=function(a){//android 需要包一层函数
                    window.androidTyyCallback.taiYYNative(a)
                }
                fn(op)
            }
        }catch(e){
            console.log(S_LOG_PREFIX,e)
        
        }         
    }
}).apply(window);

    